CodeMirror.defineMode("xml", function(config, parserConfig) {

  var indentUnit = config.indentUnit;

  var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;

  var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag || true;



  var Kludges = parserConfig.htmlMode ? {

    autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,

                      'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,

                      'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,

                      'track': true, 'wbr': true},

    implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,

                       'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,

                       'th': true, 'tr': true},

    contextGrabbers: {

      'dd': {'dd': true, 'dt': true},

      'dt': {'dd': true, 'dt': true},

      'li': {'li': true},

      'option': {'option': true, 'optgroup': true},

      'optgroup': {'optgroup': true},

      'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,

            'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,

            'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,

            'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,

            'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},

      'rp': {'rp': true, 'rt': true},

      'rt': {'rp': true, 'rt': true},

      'tbody': {'tbody': true, 'tfoot': true},

      'td': {'td': true, 'th': true},

      'tfoot': {'tbody': true},

      'th': {'td': true, 'th': true},

      'thead': {'tbody': true, 'tfoot': true},

      'tr': {'tr': true}

    },

    doNotIndent: {"pre": true},

    allowUnquoted: true,

    allowMissing: true

  } : {

    autoSelfClosers: {},

    implicitlyClosed: {},

    contextGrabbers: {},

    doNotIndent: {},

    allowUnquoted: false,

    allowMissing: false

  };

  var alignCDATA = parserConfig.alignCDATA;



  // Return variables for tokenizers

  var tagName, type;



  function inText(stream, state) {

    function chain(parser) {

      state.tokenize = parser;

      return parser(stream, state);

    }



    var ch = stream.next();

    if (ch == "<") {

      if (stream.eat("!")) {

        if (stream.eat("[")) {

          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));

          else return null;

        } else if (stream.match("--")) {

          return chain(inBlock("comment", "-->"));

        } else if (stream.match("DOCTYPE", true, true)) {

          stream.eatWhile(/[\w\._\-]/);

          return chain(doctype(1));

        } else {

          return null;

        }

      } else if (stream.eat("?")) {

        stream.eatWhile(/[\w\._\-]/);

        state.tokenize = inBlock("meta", "?>");

        return "meta";

      } else {

        var isClose = stream.eat("/");

        tagName = "";

        var c;

        while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;

        if (!tagName) return "error";

        type = isClose ? "closeTag" : "openTag";

        state.tokenize = inTag;

        return "tag";

      }

    } else if (ch == "&") {

      var ok;

      if (stream.eat("#")) {

        if (stream.eat("x")) {

          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");

        } else {

          ok = stream.eatWhile(/[\d]/) && stream.eat(";");

        }

      } else {

        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");

      }

      return ok ? "atom" : "error";

    } else {

      stream.eatWhile(/[^&<]/);

      return null;

    }

  }



  function inTag(stream, state) {

    var ch = stream.next();

    if (ch == ">" || (ch == "/" && stream.eat(">"))) {

      state.tokenize = inText;

      type = ch == ">" ? "endTag" : "selfcloseTag";

      return "tag";

    } else if (ch == "=") {

      type = "equals";

      return null;

    } else if (ch == "<") {

      return "error";

    } else if (/[\'\"]/.test(ch)) {

      state.tokenize = inAttribute(ch);

      state.stringStartCol = stream.column();

      return state.tokenize(stream, state);

    } else {

      stream.eatWhile(/[^\s\u00a0=<>\"\']/);

      return "word";

    }

  }



  function inAttribute(quote) {

    var closure = function(stream, state) {

      while (!stream.eol()) {

        if (stream.next() == quote) {

          state.tokenize = inTag;

          break;

        }

      }

      return "string";

    };

    closure.isInAttribute = true;

    return closure;

  }



  function inBlock(style, terminator) {

    return function(stream, state) {

      while (!stream.eol()) {

        if (stream.match(terminator)) {

          state.tokenize = inText;

          break;

        }

        stream.next();

      }

      return style;

    };

  }

  function doctype(depth) {

    return function(stream, state) {

      var ch;

      while ((ch = stream.next()) != null) {

        if (ch == "<") {

          state.tokenize = doctype(depth + 1);

          return state.tokenize(stream, state);

        } else if (ch == ">") {

          if (depth == 1) {

            state.tokenize = inText;

            break;

          } else {

            state.tokenize = doctype(depth - 1);

            return state.tokenize(stream, state);

          }

        }

      }

      return "meta";

    };

  }



  var curState, curStream, setStyle;

  function pass() {

    for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);

  }

  function cont() {

    pass.apply(null, arguments);

    return true;

  }



  function pushContext(tagName, startOfLine) {

    var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);

    curState.context = {

      prev: curState.context,

      tagName: tagName,

      indent: curState.indented,

      startOfLine: startOfLine,

      noIndent: noIndent

    };

  }

  function popContext() {

    if (curState.context) curState.context = curState.context.prev;

  }



  function element(type) {

    if (type == "openTag") {

      curState.tagName = tagName;

      curState.tagStart = curStream.column();

      return cont(attributes, endtag(curState.startOfLine));

    } else if (type == "closeTag") {

      var err = false;

      if (curState.context) {

        if (curState.context.tagName != tagName) {

          if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {

            popContext();

          }

          err = !curState.context || curState.context.tagName != tagName;

        }

      } else {

        err = true;

      }

      if (err) setStyle = "error";

      return cont(endclosetag(err));

    }

    return cont();

  }

  function endtag(startOfLine) {

    return function(type) {

      var tagName = curState.tagName;

      curState.tagName = curState.tagStart = null;

      if (type == "selfcloseTag" ||

          (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {

        maybePopContext(tagName.toLowerCase());

        return cont();

      }

      if (type == "endTag") {

        maybePopContext(tagName.toLowerCase());

        pushContext(tagName, startOfLine);

        return cont();

      }

      return cont();

    };

  }

  function endclosetag(err) {

    return function(type) {

      if (err) setStyle = "error";

      if (type == "endTag") { popContext(); return cont(); }

      setStyle = "error";

      return cont(arguments.callee);

    };

  }

  function maybePopContext(nextTagName) {

    var parentTagName;

    while (true) {

      if (!curState.context) {

        return;

      }

      parentTagName = curState.context.tagName.toLowerCase();

      if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||

          !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {

        return;

      }

      popContext();

    }

  }



  function attributes(type) {

    if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}

    if (type == "endTag" || type == "selfcloseTag") return pass();

    setStyle = "error";

    return cont(attributes);

  }

  function attribute(type) {

    if (type == "equals") return cont(attvalue, attributes);

    if (!Kludges.allowMissing) setStyle = "error";

    else if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}

    return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();

  }

  function attvalue(type) {

    if (type == "string") return cont(attvaluemaybe);

    if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}

    setStyle = "error";

    return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();

  }

  function attvaluemaybe(type) {

    if (type == "string") return cont(attvaluemaybe);

    else return pass();

  }



  return {

    startState: function() {

      return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, tagStart: null, context: null};

    },



    token: function(stream, state) {

      if (!state.tagName && stream.sol()) {

        state.startOfLine = true;

        state.indented = stream.indentation();

      }

      if (stream.eatSpace()) return null;



      setStyle = type = tagName = null;

      var style = state.tokenize(stream, state);

      state.type = type;

      if ((style || type) && style != "comment") {

        curState = state; curStream = stream;

        while (true) {

          var comb = state.cc.pop() || element;

          if (comb(type || style)) break;

        }

      }

      state.startOfLine = false;

      return setStyle || style;

    },



    indent: function(state, textAfter, fullLine) {

      var context = state.context;

      // Indent multi-line strings (e.g. css).

      if (state.tokenize.isInAttribute) {

        return state.stringStartCol + 1;

      }

      if ((state.tokenize != inTag && state.tokenize != inText) ||

          context && context.noIndent)

        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;

      // Indent the starts of attribute names.

      if (state.tagName) {

        if (multilineTagIndentPastTag)

          return state.tagStart + state.tagName.length + 2;

        else

          return state.tagStart + indentUnit * multilineTagIndentFactor;

      }

      if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;

      if (context && /^<\//.test(textAfter))

        context = context.prev;

      while (context && !context.startOfLine)

        context = context.prev;

      if (context) return context.indent + indentUnit;

      else return 0;

    },



    electricChars: "/",

    blockCommentStart: "<!--",

    blockCommentEnd: "-->",



    configuration: parserConfig.htmlMode ? "html" : "xml",

    helperType: parserConfig.htmlMode ? "html" : "xml"

  };

});



CodeMirror.defineMIME("text/xml", "xml");

CodeMirror.defineMIME("application/xml", "xml");

if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))

  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});

